//
// Created by Administrator on 2022/11/7.
//
#include <unistd.h>
#include "server.h"
static const char* getFileType(const char* name) {
    const char* dot = strrchr(name, '.');
    if(dot == NULL){
        return "text/plain; charset=utf-8";
    }
    if(strcmp(dot, ".html") == 0 || strcmp(dot, "htm") == 0)
        return "text/html; charset=utf-8";
    if(strcmp(dot, ".jpg") == 0 || strcmp(dot, "jpeg") == 0)
        return "image/jpeg";
    return "text/plain; charset=utf-8";
}
int initListenFd(int port){
    // 创建监听fd
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd == -1){
        perror("socket");
        return -1;
    }
    // 设置端口复用
    int opt = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1){
        perror("setsocket");
        return -1;
    }
    // 绑定IP和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(sfd, (struct sockaddr *)&addr, sizeof addr) == -1) {
        perror("bind");
        return -1;
    }
    // 启动监听
    if(listen(sfd, 128) == -1){
        perror("listen");
        return -1;
    }
    return sfd;
}

int epollRun(int lfd) {
    int epfd = epoll_create(1);
    if(epfd == -1) {
        perror("epoll create");
        return -1;
    }
    struct epoll_event ev;
    ev.data.fd = lfd;
    ev.events = EPOLLIN|EPOLLET;
    int ret =  epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if(ret == -1) {
        perror("epoll_ctl");
        return -1;
    }
    // 检查
    struct epoll_event evs[1024];
    int size = sizeof(evs) / sizeof(struct epoll_event);
    while (1) {
        int wait = epoll_wait(epfd, evs, size, -1);
        if(wait == -1){
            perror("epoll_wait");
            return -1;
        }
        printf("epoll监控到动作%d\n", wait);
        for (int i = 0; i < wait; ++i) {
            struct FdInfo * info = (struct FdInfo *)malloc(sizeof(struct FdInfo));
            int fd = evs[i].data.fd;
            info->epfd = epfd;
            info->fd = fd;
            if(fd == lfd) {
                // 建立新链接
                printf("创建链接\n");
                pthread_create(&info->pid, NULL, acceptClient, info);
            } else {
                // 数据通信
                printf("数据通信\n");
                pthread_create(&info->pid, NULL, recvHttpRequest, info);
            }
        }
    }
}
// int acceptClient(int lfd, int epfd){
void* acceptClient(void *args){
    struct FdInfo *info = (struct FdInfo*)args;
    int cfd = accept(info->fd, NULL, NULL);
    if(cfd == -1){
        perror("accept");
        return NULL;
    }
    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);
    // 添加epoll
    struct epoll_event ev;
    ev.data.fd = cfd;
    ev.events = EPOLLIN|EPOLLET;
    int ret =  epoll_ctl(info->epfd, EPOLL_CTL_ADD, cfd, &ev);
    if(ret == -1) {
        perror("epoll_ctl");
        return NULL;
    }
    printf("info thread is is %ld\n", info->pid);
    free(info);
}
// int recvHttpRequest(int cfd, int epfd)
void* recvHttpRequest(void *args) {
    struct FdInfo *info = (struct FdInfo *)args;
    printf("开始接收数据了...\n");
    int len = 0, totle = 0;
    char tmp[1024] = { 0 };
    char buf[BUF_SIZE] = { 0 };
    while ((len = recv(info->fd, tmp, sizeof tmp, 0)) > 0) {
        if(totle + len < sizeof buf){
            memcpy(buf+totle, tmp, len);
        }
        totle += len;
    }
    if(len == -1 && errno == EAGAIN){
        // 解析请求行
        char * pt = strstr(buf, CRLF);
        int reqLen = pt - buf;
        buf[reqLen] = '\0';
        parseRequesterLine(buf, info->fd);
    } else if(len == 0){
        epoll_ctl(info->epfd, EPOLL_CTL_DEL, info->fd, NULL);
        printf("关闭cfd\n");
        close(info->fd);
    } else {
        perror("recv");
    }
    printf("info thread is is %ld\n", info->pid);
    free(info);
    close(info->fd);
}
int parseRequesterLine(const char* line, int cfd){
    char method[12];
    char path[PATH_MAX];
    sscanf(line, "%[^ ] %[^ ]", method, path);
    if(strcasecmp(method, "get") != 0){
        return -1;
    }
    char *file = NULL;
    if(strcmp(path, "./")==0 || strcmp(path, "/")==0){
        file = "./";
    }else{
        file = path + 1;
    }
    struct stat st;
    int ret = stat(file, &st);
    if(ret == -1){
        // 文件不存在
        sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1);
        sendFiles("404.html", cfd);
        return 0;
    }
    if(S_ISDIR(st.st_mode)){
        // 把这个目录发给客户端
        sendHeadMsg(cfd, 200, "OK", getFileType(".html"), -1);
        sendDir(file, cfd);
    } else {
        // 把文件发送给客户端
        // 文件不存在
        sendHeadMsg(cfd, 200, "OK", getFileType(file), st.st_size);
        sendFiles(file, cfd);
    }
    return 0;
}

int sendFiles(const char* fileName, int cfd) {
    int ffd = open(fileName, O_RDONLY);
    if(ffd < 0){
        perror("sendFiles");
    }
#if 0
    while (1) {
        char buf[1024];
        int len = read(ffd, buf, sizeof buf);
        if(len > 0) {
            send(cfd, buf, len, 0);
            usleep(10);
        }else if(len == 0){
            break;
        } else{
            perror("read");
        }
    }
#else
    off_t offset = 0;
    int size = lseek(ffd, 0, SEEK_END);
    lseek(ffd, 0, 0);
    while(offset < size) {
        int ret = sendfile(cfd, ffd, &offset, size);
        if(ret == -1 && errno == EAGAIN){
            continue;
        } else if(ret == -1){
            perror("sendfile");
        }
    }
#endif
    return 0;
}
int sendHeadMsg(int cfd, int status, const char* descr, const char* type, int length){
    char buf[BUF_SIZE] = { 0 };
    sprintf(buf, "http/1.1 %d %s"CRLF, status, descr);
    sprintf(buf + strlen(buf),  "content-type: %s"CRLF, type);
    sprintf(buf + strlen(buf),  "content-length: %d"HEAD_TAIL, length);
    send(cfd, buf, strlen(buf), 0);
    return 0;
}
int sendDir(const char* fileName, int cfd) {
    char buf[BUF_SIZE] = { 0 };
    sprintf(buf, "<html><head><title>%s</title></head><body><table>", fileName);
    struct dirent** namelist;
    int num = scandir(fileName, &namelist, NULL, NULL);
    for (int i = 0; i < num; ++i) {
        char *name = namelist[i]->d_name;
        char subPath[PATH_MAX] = { 0 };
        struct stat st;
        sprintf(subPath, "%s/%s", fileName, name);
        stat(name, &st);
        if(S_ISDIR(st.st_mode)){
            sprintf(buf+ strlen(buf), "<tr><td><a href=\"%s\">%s</a></td><td>%d</td><td>%ld</td></tr>", name, name, st.st_mode, st.st_size);
        }else{
            sprintf(buf+ strlen(buf), "<tr><td><a href=\"%s\">%s</a></td><td>%d</td><td>%ld</td></tr>", name, name, st.st_mode, st.st_size);
        }
        send(cfd, buf, strlen(buf), 0);
        memset(buf, 0, strlen(buf));
        free(namelist[i]);
    }
    sprintf(buf, "</table></body></html>");
    send(cfd, buf, strlen(buf), 0);
    free(namelist);
    return 0;
}